package com.mytx.search.controller;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mytx.common.annotation.SerializedField;
import com.mytx.common.config.MytxConfig;
import com.mytx.common.constant.Constant;
import com.mytx.common.pojo.CommonResult;
import com.mytx.common.pojo.RedisLock;
import com.mytx.common.utils.DateUtils;
import com.mytx.common.utils.IDUtils;
import com.mytx.mapper.*;
import com.mytx.pojo.*;
import com.mytx.search.SearchService;
import com.mytx.search.config.EsSearchClient;
import com.mytx.search.utils.PayUtils;
import com.taobao.api.ApiException;
import com.taobao.api.TaobaoClient;
import com.taobao.api.domain.OpenImUser;
import com.taobao.api.domain.TribeInfo;
import com.taobao.api.request.OpenimTribeCreateRequest;
import com.taobao.api.response.OpenimTribeCreateResponse;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.*;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@SuppressWarnings("ALL")
@RestController
public class SearchController implements SearchService {
    private static final Logger logger = LoggerFactory.getLogger(SearchController.class);
    private static final long QUERRY_COUNT = 100;
    //id无效
    private static final int DOC_ID_INVALID = 8000;
    // 参数无效
    private static final int PARAM_INVALID = 8001;
    //ASC正序排列  DESC 倒序排列
    private static final String SORT_ORDER_ASC = "0";


    @Autowired
    private CommentMapper commentMapper;
    @Autowired
    TopicMapper topicMapper;
    @Autowired
    LiveRoomMapper liveRoomMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    EsSearchClient searchClient;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    MyFileMapper myFileMapper;
    @Value("${phone_number_match}")
    String phoneNumberMatch;

    @Value("${ali.appKey}")
    private String appKey;

    @Value("${system_manager_id}")
    private String systemManagerId;


    @Autowired
    CouponMapper couponMapper;

    @Autowired
    OrdersMapper ordersMapper;

    @Autowired
    UserMapper userMapper;

    @Autowired
    TaskMapper taskMapper;

    @Autowired
    private TaobaoClient taobaoClient;



    private static Integer count; //管理员个数

    private static Integer reqCount = 0;//用户请求的次数


    @SerializedField(encrypt = false)
    @RequestMapping(value = "/index", method = RequestMethod.POST)
    public CommonResult index(@RequestBody JSONObject body) {

        //String uId = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        String uId = request.getHeader("uId");
        Topic topic = body.getObject("topic", Topic.class);
        String topicId;
        if (StringUtils.isEmpty(topic.getId())) {
            topicId = IDUtils.genOrderItemId();
            topic.setId(topicId);
        } else {
            topicId = topic.getId();
        }
        if (topic.getStatus() == null) {
            topic.setStatus(Constant.TOPIC_STATUS_NORMAL);
        }
        Date currentDate = new Date();
        topic.setCreated(currentDate);

        Integer type = topic.getType();
        BigDecimal reward = topic.getPrice();
        BigDecimal mysqlDeposit = null;
        if (type != Constant.TOPIC_TYPE_USER_INFO) {//当类型不是用户信息类似的时候，才保存进mysql
            topic.setuId(uId);
            topic.setManagerId(uId);
            saveFile(uId, topic, currentDate);

            if (type == Constant.TOPIC_TYPE_AUCTION) {//如果是拍卖类型保存一份进redis 。出价的时候会用到
                redisTemplate.opsForValue().set(Constant.TOPIC_AUCTION + topicId, topic);
                //根据 拍卖截止日期 生成key,把这个主题的id 保存进一个set里面，以便定时任务中根据当前时间点生成key,去redis 获取需要结算的拍卖信息，并作出结算处理

                String key = generateKeyOneMinute(topic.getStopTime());
                redisTemplate.opsForSet().add(Constant.AUCTION_HANDLE_RECORD + key, topic.getId());
            } else if (type == Constant.TOPIC_TYPE_REWARD) {
                String payPassword = body.getString("payPassword");
                CommonResult commonResult = PayUtils.payPrepare(payPassword, reward, getUser(uId));
                if (commonResult != null) {
                    return commonResult;
                }
                //设置保证金
                mysqlDeposit = userMapper.incrementDeposit(uId, reward);

            }
            topicMapper.insertSelective(topic);
        }
        IndexResponse response = saveInES(topic, topicId);

        if (response == null) {
            return CommonResult.build(CommonResult.STATUS_FAIL, "没有返回值，，，");
        }
        if (response.getResult() == DocWriteResponse.Result.CREATED) {

            notifyNewTopic(topic, type);
            //如果是悬赏类型，把用户最新的保证金 金额同步到缓存
            if (mysqlDeposit != null) {
                redisTemplate.opsForHash().put(Constant.USER_SESSION + uId, Constant.DEPOSIT, mysqlDeposit);
                logger.info("index:发布悬赏保证金增加，uId:{},topicId:{},amount:{},deposit：{}", uId, topicId, reward, mysqlDeposit);
            }
            return CommonResult.ok(response.getId());
        }

        return CommonResult.build(CommonResult.STATUS_FAIL, "ES未知错误");
    }

    private void saveFile(String uId, Topic topic, Date currentDate) {
        List<MyFile> myFileList = topic.getMyFileList();
        List<MyFile> remoteFileList = new ArrayList<>();
        List<String> pictureList = new ArrayList<>();
        for (int i = 0; i < myFileList.size(); i++) {
            MyFile myFile = myFileList.get(i);
            String url = myFile.getUrl();
            if (myFile.getType() == Constant.FILE_TYPE_VIDEO) {
                topic.setVideo(url);
            } else {
                pictureList.add(url);
            }
            if (myFile.getStatus() == Constant.FILE_STATUS_REMOTE) {//为远程文件的，表示已经存储在自己的文件仓库中了，只需更新，不需要再次保存

                myFileList.remove(i);
                myFile.setUpdated(currentDate);
                remoteFileList.add(myFile);//远程文件，需要更新，在更新中增加引用的次数
                i--;
            } else {
                //处理新文件，设置引用次数为第一次，设置创建时间
                myFile.setuId(uId);
                myFile.setStatus(Constant.FILE_STATUS_REMOTE);
                myFile.setReferenceCount(1);//其实这儿也可以在 mysql 建表中设置为默认值为1
                myFile.setCreated(currentDate);
            }
        }
        topic.setMyFileList(null);//清空掉，不然会报存进 es
        topic.setDonationSize(0);//捐赠数量设置为0，如果是null 的情况下，在收到捐赠增加捐赠数量，会失败。
        topic.setPictures(pictureList);

        if (myFileList.size() != 0) {
            myFileMapper.insertBatch(myFileList);
        }
        if (remoteFileList.size() != 0) {
            myFileMapper.updateBatch(remoteFileList);
        }
    }

    private void notifyNewTopic(Topic topic, Integer type) {
        switch (type) {
            case Constant.TOPIC_TYPE_EXPOSE:
            case Constant.TOPIC_TYPE_REWARD:
            case Constant.TOPIC_TYPE_FOUND:
            case Constant.TOPIC_TYPE_AUCTION:
            case Constant.TOPIC_TYPE_GOODS:
            case Constant.TOPIC_TYPE_OTHER:
                JSONObject info = new JSONObject();
                info.put("topic", topic);
                redisTemplate.convertAndSend(Constant.NOTIFY_NEW_TOPIC, info);
        }
    }

    private IndexResponse saveInES(Topic topic, String topicId) {
        String jsonString = JSONObject.toJSONString(topic);
        // logger.debug("index: uid:{},topic:{}", uId, jsonString);
        IndexRequest request = new IndexRequest("topics", "topic", topicId);
        // request.opType(DocWriteRequest.OpType.UPDATE);
        request.source(jsonString, XContentType.JSON);
        return searchClient.index(request);
    }

    @SerializedField(encrypt = false)
    @RequestMapping(value = "/get", method = RequestMethod.POST)
    public CommonResult get(@RequestBody String topicId) {
        logger.info("getTopicById:topicId{}", topicId);
        if (StringUtils.isBlank(topicId)) {
            return CommonResult.build(PARAM_INVALID, "参数无效");
        }
        GetRequest request = new GetRequest("topics", "topic", topicId);
        GetResponse response = searchClient.get(request);
        if (response == null) {
            return CommonResult.build(CommonResult.STATUS_FAIL, "没有返回值，，，");
        }
        if (response.isExists()) {
            queryPraiseSize(response.getSource(), topicId);
            //查询redis 中的点赞数量，再加上存在 es 中的点赞数量，得到主题全部点赞数量
//            String praise = redisTemplate.opsForValue().getTopicById(Constant.TOPIC_PRAISE_SIZE + topicId);
//            if (StringUtils.isNotEmpty(praise)) {
//
//                if (response.getSource().containsKey("praiseSize")) {
//                    int originalPraiseSize = (int) response.getSource().getTopicById(" praiseSize");
//                    response.getSource().put("praiseSize", originalPraiseSize + Integer.valueOf(praise));
//                } else {
//                    response.getSource().put("praiseSize", praise);
//                }
//            }
            return CommonResult.ok(response.getSource());
        } else {
            return CommonResult.build(DOC_ID_INVALID, "文档ID无效");
        }

    }

    @RequestMapping(value = "/get_v1", method = RequestMethod.POST)
    public CommonResult getTopicFromMysql(@RequestBody String topicId) {
        Topic topic = topicMapper.selectFullTopicByPrimaryKey(topicId);
        return CommonResult.ok(topic);
    }


    @SerializedField(encrypt = false)
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    public CommonResult update(@RequestBody Topic topic) {
        String uId = request.getHeader("uId");
        Date currentDate = new Date();
        List<MyFile> myFileList = topic.getMyFileList();
        topic.setMyFileList(null);//清空，不然会保存进es
        if (myFileList != null && myFileList.size() != 0) {
            List<String> pictureList = new ArrayList<>();

            for (int i = 0; i < myFileList.size(); i++) {
                MyFile myFile = myFileList.get(i);
                String url = myFile.getUrl();
                if (myFile.getType() == Constant.FILE_TYPE_VIDEO) {
                    topic.setVideo(url);
                } else {
                    pictureList.add(url);
                }
                if (myFile.getStatus() == Constant.FILE_STATUS_REMOTE) {//远程文件不需要再次保存，也不需要更新
                    myFileList.remove(i);
                    i--;
                } else {
                    //处理新文件，设置引用次数为第一次，设置创建时间
                    myFile.setuId(uId);
                    myFile.setStatus(Constant.FILE_STATUS_REMOTE);
                    myFile.setReferenceCount(1);//其实这儿也可以在 mysql 建表中设置为默认值为1
                    myFile.setCreated(currentDate);
                }
            }
            if (myFileList.size() != 0) {
                myFileMapper.insertBatch(myFileList);
            }
            topic.setPictures(pictureList);
        }
        topic.setMyFileList(null);//去掉文件，不然会报存进es
        Integer type = topic.getType();
        if(type!=null) {
            if ((type == Constant.TOPIC_TYPE_EXPOSE || type == Constant.TOPIC_TYPE_OTHER || type == Constant.TOPIC_TYPE_SCHOOL ||
                    type == Constant.TOPIC_TYPE_AREA || type == Constant.TOPIC_TYPE_INDUSTRY) && (topic.getStatus() == Constant.TOPIC_STATUS_NORMAL) || topic.getStatus() == Constant.TOPIC_STATUS_DELETE) {
                topicMapper.updateByPrimaryKeySelective(topic);
                //修改聊天室的背景图片
                if ((type == Constant.TOPIC_TYPE_SCHOOL || type == Constant.TOPIC_TYPE_AREA || type == Constant.TOPIC_TYPE_INDUSTRY) && topic.getStatus() == Constant.TOPIC_STATUS_NORMAL) {
                    List<String> pictures = topic.getPictures();
                    LiveRoom liveRoom = new LiveRoom();
                    liveRoom.setTitle(topic.getTitle());
                    liveRoom.setId(topic.getId());
                    if(pictures.size()!=0){
                        liveRoom.setCover(pictures.get(0));
                    }else{
                        liveRoom.setCover("");
                    }

                    liveRoom.setVideo(topic.getVideo());
                    liveRoomMapper.updateByPrimaryKeySelective(liveRoom);
                }
            }
        }
        //如果为聊天室主题,还要修改
//        if (topic.getVideo() == null) {//如果没有设置视频，用一个空代替，如果之前有视频连接，就空替换了，客户端需要把空格 和null 都识别为没有视频
//            topic.setVideo("");
//        }
        String jsonString = JSONObject.toJSONString(topic);
        UpdateRequest request = new UpdateRequest("topics", "topic", topic.getId());
        request.doc(jsonString, XContentType.JSON);
        UpdateResponse response = searchClient.update(request);
        if (response == null) {
            return CommonResult.build(CommonResult.STATUS_FAIL, "没有返回值，，，");
        }
        if (response.getResult() == DocWriteResponse.Result.UPDATED || response.getResult() == DocWriteResponse.Result.NOOP || response.getResult() == DocWriteResponse.Result.CREATED) {
            return CommonResult.ok();
        } else {
            logger.error("update：更新失败,result:{}", response.getResult());
            return CommonResult.build(DOC_ID_INVALID, "更新失败：" + response.getResult());
        }

    }



    @SerializedField(encrypt = false)
    @RequestMapping(value = "/rewardTopicDelete", method = RequestMethod.POST)
    public CommonResult rewardTopicDelete(@RequestBody Topic topic) {
        String uId = request.getHeader("uId");
        //判断悬赏任务是否还有被处理的任务
        Topic tabletopic = topicMapper.selectByPrimaryKey(topic.getId());
        //判断悬赏主题是否已经撤销了
        if (Constant.TOPIC_STATUS_DELETE ==tabletopic.getStatus()) {
            return CommonResult.build(Constant.REWARD_TOPIC_ALREADY_DELETE, "主题已经删除了,不可重复删除");
        }
        Integer tableTopicType = tabletopic.getType();
        if (Constant.TOPIC_TYPE_REWARD == tableTopicType) {
            TaskExample taskExample = new TaskExample();
            TaskExample.Criteria criteria = taskExample.createCriteria();
            criteria.andTIdEqualTo(topic.getId());
            List<Task> taskList = taskMapper.selectByExample(taskExample);
            //3.判断悬赏任务下面是否已经有人接受了任务
            for (Task task : taskList) {
                Integer taskStatus = task.getStatus();
                if (Constant.TASK_STATUS_CONFIRM == taskStatus || Constant.TASK_STATUS_EMPLOYER_CANCEL == taskStatus || Constant.TASK_STATUS_EMPLOYEE_REFUSE_CANCEL == taskStatus
                        || Constant.TASK_STATUS_APPLY_FINISH_TASK == taskStatus || Constant.TASK_STATUS_EMPLOYER_REFUSE_FINISH_APPLY == taskStatus) {
                    // 如果有人接受了任务，则不能取消任务
                    logger.info("cancelOffeTopic:取消的悬赏主题下面还有未被处理的任务,不能删除悬赏主题");
                    return CommonResult.build(Constant.REWARD_TOPIC_STATUS_ERROR, "您还有没有被处理的任务,不能删除悬赏任务");
                }
            }
        }
        //将数据库中的状态设置为删除状态
        Topic newTopic = new Topic();
        newTopic.setId(topic.getId());
        newTopic.setStatus(Constant.TOPIC_STATUS_DELETE);
        topicMapper.updateByPrimaryKeySelective(newTopic);
        //如果为聊天室主题，并且为未审核状态或者审核不通过，不去ES中删除数据
        if ((tableTopicType==Constant.TOPIC_TYPE_SCHOOL||tableTopicType==Constant.TOPIC_TYPE_AREA||tableTopicType==Constant.TOPIC_TYPE_INDUSTRY)
                &&(tabletopic.getStatus()==Constant.TOPIC_STATUS_VERIFY||tabletopic.getStatus()==Constant.TOPIC_STATUS_VERIFY_FAILE)){
            return CommonResult.ok();
        }
        return update(topic);

    }

    @SerializedField(encrypt = false)
    @PostMapping("/deleteCoupon")
    public CommonResult deleteCoupon(@RequestBody String topicId) {
        UpdateRequest request = new UpdateRequest("topics", "topic", topicId);
        Script script = new Script("ctx._source.remove(\"coupon\")");
        request.script(script);
        UpdateResponse response = searchClient.update(request);
        if (response == null) {
            return CommonResult.build(CommonResult.STATUS_FAIL, "没有返回值，，，");
        }
        return CommonResult.ok();
    }

    /**
     * 根据删除指定主题的字段信息
     *
     * @param info
     * @return
     */
    @PostMapping("/deleteField")
    public CommonResult deleteField(@RequestBody Map<String, Object> info) {
        String topicId = (String) info.get("topicId");
        List<String> fieldNameList = (List) info.get("fieldName");
        for (String fieldName : fieldNameList) {
            UpdateRequest request = new UpdateRequest("topics", "topic", topicId);
            Script script = new Script("ctx._source.remove(\"" + fieldName + "\")");
            request.script(script);
            UpdateResponse response = searchClient.update(request);
            if (response == null) {
                return CommonResult.build(CommonResult.STATUS_FAIL, "没有返回值，，，");
            }
        }
        return CommonResult.ok();
    }

    @SerializedField(encrypt = false)
    @RequestMapping(value = "/delete", method = RequestMethod.DELETE)
    public CommonResult delete(@RequestBody Map<String, Object> info) {
        String id = info.get("id") + "";
        if (StringUtils.isBlank(id)) {
            return CommonResult.build(PARAM_INVALID, "参数无效");
        }

        DeleteRequest request = new DeleteRequest("topics", "topic", id);

        DeleteResponse response = searchClient.delete(request);
        if (response == null) {
            return CommonResult.build(CommonResult.STATUS_FAIL, "没有返回值，，，");
        }
        if (response.getResult() == DocWriteResponse.Result.DELETED) {
            return CommonResult.ok();
        } else {
            return CommonResult.build(DOC_ID_INVALID, "文档id无效");
        }
    }

    @SerializedField(encrypt = false)
    @RequestMapping(value = "/search", method = RequestMethod.POST)
    public CommonResult search(@RequestBody Map<String, Object> info) {
        logger.info("search:" + info);
        String queryContent = info.get("queryContent") + "";
        if (!info.containsKey("queryContent")) {
            return CommonResult.build(PARAM_INVALID, "参数无效");
        }
        SearchRequest request = new SearchRequest("topics");//topics是别名,索引名是 topic_v1,类型是：topic
        request.types("topic");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        MultiMatchQueryBuilder multiMatchQueryBuilder = null;
        if (isPhoneNumber(queryContent)) {
//            TermQueryBuilder phoneQueryBuilder = QueryBuilders.termQuery("author.phone", queryContent).boost(2f); //分开写，给单独的查询设置权重
//            boolQueryBuilder.must(phoneQueryBuilder);
            multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(queryContent, "title", "content", "manager.phone");
            multiMatchQueryBuilder.field("manager.phone", 20f); //增加author.phone 字段的权重,就算是设置10，权重都抵不过 同时title、content 字段都匹配上的
        } else {
            multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(queryContent, "title", "content");
        }
        multiMatchQueryBuilder.field("title", 1.5f); //增加title 字段的权重，默认boost都是 1
        multiMatchQueryBuilder.field("content", 0.5f); //降低content 字段的权重，默认boost都是 1

        boolQueryBuilder.must(multiMatchQueryBuilder);

        TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("status", (Object) Constant.TOPIC_STATUS_NORMAL, (Object) Constant.TOPIC_STATUS_REWARD_COMPLETE);
        boolQueryBuilder.filter(termsQueryBuilder);
        if (info.containsKey("lon")) {
            GeoDistanceQueryBuilder distanceQueryBuilder = QueryBuilders.geoDistanceQuery("location")
                    .distance((String) info.get("distance"), DistanceUnit.KILOMETERS)
                    .point((double) info.get("lat"), (double) info.get("lon"));
            boolQueryBuilder.must(distanceQueryBuilder);
        }
        searchSourceBuilder.query(boolQueryBuilder);


        if (info.containsKey("from")) {
            searchSourceBuilder.from((Integer) info.get("from"));
        }
        if (info.containsKey("size")) {
            searchSourceBuilder.size((Integer) info.get("size"));
        }

        request.source(searchSourceBuilder);
        SearchResponse searchResponse = searchClient.search(request);
        CommonResult result = getCommonResult(searchResponse, false);
        if (result != null) return result;
        return CommonResult.build(CommonResult.STATUS_FAIL, "查不到");
    }

    /**
     * app进入mapFragment的时候，定位成功，自动搜索
     *
     * @param body
     * @return
     */
    @SerializedField(encrypt = false)
    @PostMapping("/autoSearch")
    public CommonResult autoSearch(@RequestBody JSONObject body) {
        String uId = request.getHeader("uId");
        Double latitude = body.getDouble("lat");
        Double longitude = body.getDouble("lon");
        LatLng latLng = new LatLng(latitude, longitude);
        Topic updateTopic = new Topic();
        updateTopic.setId(uId);
        updateTopic.setLocation(latLng);
        update(updateTopic);
        return nearbySearch(body);
    }

    /**
     * 搜索附近，不传搜索关键词，
     * 按地区 行业 学校  3个类型进行搜索
     *
     * @param body
     * @return
     */
    @SerializedField(encrypt = false)
    @PostMapping("/nearbySearch")
    public CommonResult nearbySearch(@RequestBody JSONObject body) {
        //  String uId = request.getHeader("uId");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
//        boolQueryBuilder.should(matchAllQueryBuilder);

        TermQueryBuilder statusQueryBuilder = QueryBuilders.termQuery("status", Constant.TOPIC_STATUS_NORMAL);//(Object) Constant.TOPIC_STATUS_REWARD_COMPLETE
        boolQueryBuilder.filter(statusQueryBuilder);
       /* TermQueryBuilder school = QueryBuilders.termQuery("type", Constant.TOPIC_TYPE_SCHOOL);
        boolQueryBuilder.should(school);
        TermQueryBuilder industry = QueryBuilders.termQuery("type", Constant.Constant.TOPIC_TYPE_SCHOOL);
        boolQueryBuilder.should(industry);
        TermQueryBuilder area = QueryBuilders.termQuery("type", Constant.TOPIC_TYPE_AREA);
        boolQueryBuilder.should(area);*/
        TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("type", (Object) Constant.TOPIC_TYPE_SCHOOL,
                (Object) Constant.TOPIC_TYPE_SCHOOL,
                (Object) Constant.TOPIC_TYPE_AREA,
                (Object) Constant.TOPIC_TYPE_EXPOSE,
                (Object) Constant.TOPIC_TYPE_REWARD,
                (Object) Constant.TOPIC_TYPE_OTHER);
        boolQueryBuilder.filter(termsQueryBuilder);
        int distance = 3;
        if (body.containsKey("distance")) {
            distance = body.getInteger("distance");
        }
        GeoDistanceQueryBuilder distanceQueryBuilder = QueryBuilders.geoDistanceQuery("location")
                .distance(distance, DistanceUnit.KILOMETERS)
                .point(body.getDouble("lat"), body.getDouble("lon"));
        boolQueryBuilder.must(distanceQueryBuilder);
        searchSourceBuilder.query(boolQueryBuilder);
        searchSourceBuilder.sort("commentSize", SortOrder.DESC);
        searchSourceBuilder.sort("created", SortOrder.DESC);
        Integer page = body.getInteger("page");
        boolean isTextList = false;
        if (page != null) {
            searchSourceBuilder.from((page - 1) * 10).size(10);
            isTextList = true;
        } else {
            searchSourceBuilder.size(10);
        }
        SearchRequest searchRequest = new SearchRequest("topics");//topics是别名,索引名是 topic_v1,类型是：topic
        searchRequest.types("topic");
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = searchClient.search(searchRequest);
        CommonResult result = getCommonResult(searchResponse, isTextList);
        if (result != null) return result;
        return CommonResult.build(CommonResult.STATUS_FAIL, "查不到");

    }

    /**
     * 组装数据 因为在es 中，id 字段和其他字段，不是放在一起的，如果不在服务端处理，就得在客户端 端处理
     *
     * @param searchResponse
     * @return
     */
    private CommonResult getCommonResult(SearchResponse searchResponse, boolean isTextList) {
        if (searchResponse != null) {
            String uId = request.getHeader("uId");
            Map<String, Object> result = new HashMap<>();
            List<Object> resultList = new ArrayList<>();
            SearchHits hits = searchResponse.getHits();
            result.put("total", hits.getTotalHits());
            // 2019/5/4 取出id放入list 之后去redis批量查询 之后再遍历添加到map中
            List<String> topicIdList = new ArrayList<>();
            for (SearchHit searchHit : hits.getHits()) {
                Map<String, Object> item = new HashMap<>();
                String id = searchHit.getId();
                topicIdList.add(Constant.TOPIC_PRAISE_SIZE + id);
                item.put("id", id);
                Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
                if (sourceAsMap.containsKey("coupon")) {//如果包含红包,取出红包的id
                    String parentCouponId = (String) ((HashMap) sourceAsMap.get("coupon")).get("id");
                    Boolean member = redisTemplate.opsForSet().isMember(Constant.COUPON_RECEIVED_RECORD + parentCouponId, uId);
                    if (member) {//如果已经领取过该红包，需要去mysql 取出子红包 ，返回给客户端
                        //  Coupon childCoupon = couponMapper.selectChildCoupon_v1(parentCouponId, uId);
                        // childCoupon.setParentId(parentCouponId);
                        //  sourceAsMap.put("coupon", childCoupon);

                        //只返回已经领取的标识，不再返回 用户领取的子红包，节约数据库资源，
                        ((HashMap) sourceAsMap.get("coupon")).put("status", Constant.COUPON_STATUS_RECEIVED);
                    }
                }

                if (sourceAsMap.containsKey("type")) {
                    Integer type = (Integer) sourceAsMap.get("type");

                    if (type == Constant.TOPIC_TYPE_REWARD) {
                        Boolean member = redisTemplate.opsForSet().isMember("TASK_RECEIVE_RECORD:" + id, uId);
                        sourceAsMap.put("isReceived", member);
                    }
                }
                item.putAll(sourceAsMap);
                resultList.add(item);
            }
            if (isTextList) {
                List praiselist = redisTemplate.opsForValue().multiGet(topicIdList);
                for (int i = 0; i < resultList.size(); i++) {
                    Map<String, Object> item = (Map<String, Object>) resultList.get(i);
                    item.put("praiseSize", praiselist.get(i));
                }
            }
            result.put("hits", resultList);
            return CommonResult.ok(result);
        }
        return null;
    }

    @SerializedField(encrypt = false)
    @RequestMapping(value = "/matchAll", method = RequestMethod.POST)
    public CommonResult matchAll(@RequestBody Map<String, Object> info) {
        SearchRequest request = new SearchRequest("topics");
        request.types("topic");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        TermQueryBuilder termQuery = QueryBuilders.termQuery("status", "0");
        boolQueryBuilder.mustNot(termQuery);
        searchSourceBuilder.query(boolQueryBuilder);
        //设置返回数据的起始点
        if (info.containsKey("from")) {
            searchSourceBuilder.from((Integer) info.get("from"));
        }
        //设置返回数据的长度
        if (info.containsKey("size")) {
            searchSourceBuilder.size((Integer) info.get("size"));
        }
        //排序
        if (info.containsKey("sort")) {
            searchSourceBuilder.sort(info.get("sortField").toString(), info.get("sort").toString().equals(SORT_ORDER_ASC) ? SortOrder.ASC : SortOrder.DESC);

        } else {
            //如果没有给定排序依据，默认按更新日期的倒序，最近更新的显示在最前面
            searchSourceBuilder.sort("update_date", SortOrder.DESC);

        }

        request.source(searchSourceBuilder);
        SearchResponse searchResponse = searchClient.search(request);
        CommonResult result = getCommonResult(searchResponse, false);
        if (result != null) return result;
        return CommonResult.build(CommonResult.STATUS_FAIL, "查不到");
    }

    /**
     * 用于查询自己发布了哪些商品，并且根据状态status，查询哪些商品是再售状态(status:1)，哪些商品是下架状态(status:0)
     *
     * @param info
     * @return
     */
    @RequestMapping(value = "/searchByUid", method = RequestMethod.POST)
    @SerializedField(encrypt = false)
    public CommonResult searchByUid(@RequestBody Map<String, Object> info) {
        logger.info("searchByUid:" + info.get("uId"));
        if (!info.containsKey("uId") || !info.containsKey("status")) {
            return CommonResult.build(PARAM_INVALID, "参数无效");
        }
        SearchRequest request = new SearchRequest("topics");
        request.types("topic");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //通过bool查询将两个term查询组合起来
        TermQueryBuilder queryBuilder_1 = QueryBuilders.termQuery("author.uId", info.get("uId"));
        TermQueryBuilder queryBuilder_2 = QueryBuilders.termQuery("status", info.get("status"));
        boolQueryBuilder.must(queryBuilder_1);
        boolQueryBuilder.must(queryBuilder_2);
        searchSourceBuilder.query(boolQueryBuilder);
        //设置返回数据的起始点
        if (info.containsKey("from")) {
            searchSourceBuilder.from((Integer) info.get("from"));
        }
        //设置返回数据的长度
        if (info.containsKey("size")) {
            searchSourceBuilder.size((Integer) info.get("size"));
        }
        searchSourceBuilder.sort("updated", SortOrder.DESC);
        searchSourceBuilder.sort("commentSize", SortOrder.DESC);
        searchSourceBuilder.sort("created", SortOrder.DESC);
        request.source(searchSourceBuilder);
        SearchResponse searchResponse = searchClient.search(request);
        if (searchResponse != null) {

            for (SearchHit documentFields : searchResponse.getHits()) {
                String topicId = documentFields.getId();
                queryPraiseSize(documentFields.getSourceAsMap(), topicId);

            }
        }
        CommonResult result = getCommonResult(searchResponse, false);
        if (result != null) return result;
        return CommonResult.build(CommonResult.STATUS_FAIL, "查不到");
    }

    /**
     * 查询点赞数量，
     * 先在redis 中查看未持久化的点赞数量，然后和原始点赞数量相加，就得到全部点赞数
     *
     * @param topicId
     */
    private void queryPraiseSize(Map<String, Object> sourceMap, String topicId) {
        //查询该主题的点赞数量
        Object praise = redisTemplate.opsForValue().get(Constant.TOPIC_PRAISE_SIZE + topicId);
        //主题的点赞数量是保存在 redis,这儿就用不着来
        if (praise != null) {
            if (sourceMap.containsKey("praiseSize")) {//如果es 中的topic 本身有点赞数量，那说明 redis 中的是点赞的增量，需要两个点赞数量相加，才是全部的点赞数量
                int originalPraiseSize = (int) sourceMap.get("praiseSize");
                Integer praiseSize = (Integer) praise;
                sourceMap.put("praiseSize", originalPraiseSize + praiseSize);
            } else {
                sourceMap.put("praiseSize", praise);
            }
        }
    }

    /**
     * 根据用户id ，在mysql中查询发布的topic,
     *
     * @param body
     * @return
     */
//    @SerializedField(encrypt = false)
    @PostMapping("/getTopicByUid")
    public CommonResult getTopicByUid(@RequestBody JSONObject body) {
        String uId = body.getString("uId");
        int page = body.getInteger("page");
        int size = body.getInteger("size");

        PageHelper.startPage(page, size);
        List<Topic> topicList = topicMapper.selectMyTopic(uId);
        PageInfo<Topic> pageInfo = new PageInfo<>(topicList);
        for (Topic topic : topicList) {
            Object praise = redisTemplate.opsForValue().get(Constant.TOPIC_PRAISE_SIZE + topic.getId());
            if (praise != null) {
                Integer praiseSize = (Integer) praise;
                topic.setPraiseSize(topic.getPraiseSize() + praiseSize);
            }
        }
        return CommonResult.ok(pageInfo);
    }

    /**
     * 举报，修改主题状态为举报状态 5  方便人工介入时候查询
     *
     * @param topicId
     * @return
     */
    @PostMapping("/accusationTopic")
    public CommonResult accusationTopic(@RequestBody String topicId) {
        String uId = request.getHeader("uId");
        logger.warn("accusationTopic:uId:{}  topicId{}", uId, topicId);
        // TODO: 2018/11/13 是否应该推送消息给客服人员？？立马人工接入
        Topic topic = new Topic();
        topic.setId(topicId);
        topic.setStatus(Constant.TYPE_ACCUSATION);
        topicMapper.updateByPrimaryKeySelective(topic);
        // update(topic); 不应该马上更新到ES，否则该主题只要一举报就不能被搜索了，避免有人恶意举报。而是应该通过人工介入处理
        return CommonResult.ok();
    }

    @Transactional
    @PostMapping("/createTopicAndLiveRoom")
    public CommonResult createTopicAndLiveRoom(@RequestBody Topic topic) throws ApiException {
        String uId = request.getHeader("uId");
        logger.info("createTopicAndLiveRoom:uId:{}", uId);
// TODO: 2019/6/2 群是否也应该在 通过审核后再创建呢？
        //判断聊天室是否存在，并且是否为未审核状态
        String fromTopicId = topic.getId();
        Topic tableTopic = topicMapper.selectByPrimaryKey(fromTopicId);
        if(tableTopic!=null){
            if(tableTopic.getStatus()!=Constant.TOPIC_STATUS_VERIFY_FAILE){
                return CommonResult.build(Constant.PARAMETER_INVALID,"聊天室参数不对");
            }
            Date currentDate = new Date();
            topic.setUpdated(currentDate);
            topic.setStatus(Constant.TOPIC_STATUS_VERIFY);
            saveFile(uId, topic, currentDate);
            topicMapper.updateByPrimaryKeySelective(topic);
            JSONObject info = new JSONObject();
            info.put("uId", getTopicAdminId());
            info.put("topic", topic);
            redisTemplate.convertAndSend(Constant.NOTIFY_TOPIC_VERIFY, info);
            return CommonResult.ok(fromTopicId);
        }
            OpenImUser user = new OpenImUser();
            user.setUid(uId);
            user.setAppKey(appKey);
            user.setTaobaoAccount(false);
            OpenimTribeCreateRequest createRequest = new OpenimTribeCreateRequest();
            createRequest.setUser(user);
            createRequest.setNotice(topic.getTitle());
            createRequest.setTribeName(topic.getTitle());
            createRequest.setTribeType(0L);

            OpenimTribeCreateResponse response = taobaoClient.execute(createRequest);
            if (!response.isSuccess()) {
                logger.error("createTopicAndLiveRoom:创建群失败" + response.getErrorCode() + response.getMsg());
                return CommonResult.build(CommonResult.STATUS_FAIL, "创建群失败");
            }
            TribeInfo tribeInfo = response.getTribeInfo();
            System.out.println();
            //以阿里云旺群id 为主题id
            String topicId = String.valueOf(tribeInfo.getTribeId());
            //   String topicId = IDUtils.genOrderItemId();
            Date currentDate = new Date();
            saveFile(uId, topic, currentDate);
            topic.setuId(uId);
            topic.setManagerId(uId);
            topic.setId(topicId);
            topic.setStatus(Constant.TOPIC_STATUS_VERIFY);
            topic.setCreated(currentDate);
            //未经审核的行业或者学校或者地区类型的主题，一开始并不保存进ES,仅仅保存进mysql
            topicMapper.insertSelective(topic);
        // TODO: 2019/6/3 后面考虑用一张表维护审核主题的相关的信息
            JSONObject info = new JSONObject();
            info.put("uId", getTopicAdminId());
            info.put("topic", topic);
            redisTemplate.convertAndSend(Constant.NOTIFY_TOPIC_VERIFY, info);
            return CommonResult.ok(topicId);


    }

    /**
     * 新建聊天室审核通过
     *
     * @param body
     * @return
     */
    @Transactional
    @PostMapping("/verifySuccess")
    public CommonResult verifyTopicSuccess(@RequestBody String topicId) {
        String uId = request.getHeader("uId");
        if (!(MytxConfig.topicAdminList.contains(uId))) {
            logger.error("verityTopicSuccess:权限不足，非管理员 uId:{}", uId);
            return CommonResult.build(Constant.NO_AUTHORITY, "权限不足");
        }
        Topic tableTopic = topicMapper.selectFullTopicByPrimaryKey(topicId);
        if (tableTopic == null) {
            return CommonResult.build(Constant.PARAMETER_INVALID, "找不到对应的主题");
        }
        if (tableTopic.getStatus() != Constant.TOPIC_STATUS_VERIFY) {
            return CommonResult.build(Constant.TOPIC_STATUS_ERROR, "主题状态非待审核状态");
        }

        Topic updateTopic = new Topic();
        updateTopic.setId(topicId);
        updateTopic.setStatus(Constant.TOPIC_STATUS_NORMAL);
        Date currentDate = new Date();
        updateTopic.setUpdated(currentDate);
        topicMapper.updateByPrimaryKeySelective(updateTopic);
        tableTopic.setStatus(Constant.TOPIC_STATUS_NORMAL);
        createLiveRoom(tableTopic, tableTopic.getManagerId(), topicId, currentDate);
        saveInES(tableTopic, topicId);
        notifyNewTopic(tableTopic, tableTopic.getType());
        JSONObject info = new JSONObject();
        info.put("uId", tableTopic.getManagerId());
        info.put("topic", tableTopic);
        redisTemplate.convertAndSend(Constant.NOTIFY_TOPIC_VERIFY, info);
        return CommonResult.ok();
    }

    /**
     * 新建聊天室审核失败
     *
     * @param body
     * @return
     */
    @PostMapping("/verifyFail")
    public CommonResult verifyTopicFail(@RequestBody Topic topic) {
        String uId = request.getHeader("uId");
        String topicId = topic.getId();
        if (!(MytxConfig.topicAdminList.contains(uId))) {
            logger.error("verityTopicSuccess:权限不足，非管理员 uId:{}", uId);
            return CommonResult.build(Constant.NO_AUTHORITY, "权限不足");
        }
        Topic tableTopic = topicMapper.selectFullTopicByPrimaryKey(topicId);
        if (tableTopic == null) {
            return CommonResult.build(Constant.PARAMETER_INVALID, "找不到对应的主题");
        }
        if (tableTopic.getStatus() != Constant.TOPIC_STATUS_VERIFY) {
            return CommonResult.build(Constant.TOPIC_STATUS_ERROR, "主题状态非待审核状态");
        }
        Topic newTopic = new Topic();
        newTopic.setId(topic.getId());
        newTopic.setStatus(Constant.TOPIC_STATUS_VERIFY_FAILE);
        newTopic.setUpdated(new Date());
        topicMapper.updateByPrimaryKeySelective(newTopic);
        tableTopic.setStatus(Constant.TOPIC_STATUS_VERIFY_FAILE);
        //前端把拒绝原因写在content里面，携带传进来的，也可能是 null
        tableTopic.setExtra(topic.getExtra());
        JSONObject info = new JSONObject();
        info.put("uId", tableTopic.getManagerId());
        info.put("topic", tableTopic);
        redisTemplate.convertAndSend(Constant.NOTIFY_TOPIC_VERIFY, info);
        return CommonResult.ok();
    }


    private void createLiveRoom(@RequestBody Topic topic, String uId, String topicId, Date currentDate) {

        String url = topic.getVideo();
        if (url == null) {
            url = topic.getPictures().get(0);
        }
        LiveRoom liveRoom = new LiveRoom();
        liveRoom.setId(topicId);//让直播间id 和对应的主题Id一致
        liveRoom.setTitle(topic.getTitle());
        liveRoom.setVideo(topic.getVideo());
        if (topic.getPictures() != null && topic.getPictures().size() != 0) {
            liveRoom.setCover(topic.getPictures().get(0));
        }
        liveRoom.setStatus(Constant.LIVE_ROOM_STATUS_STOPED);
        liveRoom.setManagerId(uId);
        liveRoom.setCreated(currentDate);
        liveRoom.setChatId(topicId);
        liveRoom.setCreated(new Date());
        liveRoomMapper.insertSelective(liveRoom);
    }

    @PostMapping("/searchLiveRoom")
    public CommonResult searchLiveRoom(@RequestBody Map<String, Object> info) {


        logger.info("search:" + info);
        String queryContent = info.get("queryContent") + "";

        if (!info.containsKey("queryContent")) {
            return CommonResult.build(PARAM_INVALID, "参数无效");
        }
        SearchRequest request = new SearchRequest("topics");//topics是别名,索引名是 topic_v1,类型是：topic
        request.types("topic");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(queryContent, "title");
        boolQueryBuilder.must(multiMatchQueryBuilder);
        if (info.containsKey("lon")) {
            GeoDistanceQueryBuilder distanceQueryBuilder = QueryBuilders.geoDistanceQuery("location")
                    .distance((String) info.get("distance"), DistanceUnit.KILOMETERS)
                    .point((double) info.get("lat"), (double) info.get("lon"));
            boolQueryBuilder.must(distanceQueryBuilder);
        }

        //排除验证状态的
//        TermQueryBuilder queryBuilderVerify = QueryBuilders.termQuery("status", Constant.TOPIC_STATUS_VERIFY);
//        boolQueryBuilder.mustNot(queryBuilderVerify);

        List<Integer> terms = new ArrayList<>();
        terms.add(Constant.TOPIC_TYPE_AREA);
        terms.add(Constant.TOPIC_TYPE_INDUSTRY);
        terms.add(Constant.TOPIC_TYPE_SCHOOL);
        TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("type", terms);
        boolQueryBuilder.filter(termsQueryBuilder);
        searchSourceBuilder.query(boolQueryBuilder);


        if (info.containsKey("from")) {
            searchSourceBuilder.from((Integer) info.get("from"));
        }
        if (info.containsKey("size")) {
            searchSourceBuilder.size((Integer) info.get("size"));
        }

        request.source(searchSourceBuilder);
        SearchResponse searchResponse = searchClient.search(request);
        CommonResult result = getCommonResult(searchResponse, false);
        if (result != null) return result;
        return CommonResult.build(CommonResult.STATUS_FAIL, "查不到");
    }

    @PostMapping("/queryMyFile")
    public CommonResult queryMyFile(@RequestBody Map<String, Object> info) {
        String uId = request.getHeader("uId");
        int page = 1;
        int size = 10;
        if (info.containsKey("page")) {
            page = (int) info.get("page");
        }
        if (info.containsKey("size")) {
            size = (int) info.get("size");
        }
        MyFileExample example = new MyFileExample();
        example.setOrderByClause(" reference_count DESC ,  created DESC");// 先按引用次数 降序排列，再按日期降序排列  ，最常用的，最新的显示在最前面
        MyFileExample.Criteria criteria = example.createCriteria();
        criteria.andUIdEqualTo(uId);
        PageHelper.startPage(page, size);
        List<MyFile> myFiles = myFileMapper.selectByExample(example);
        PageInfo<MyFile> offerPageInfo = new PageInfo<>(myFiles);
        return CommonResult.ok(offerPageInfo);
    }

    /**
     * 对拍卖商品出价
     *
     * @param offer
     * @return
     */
    @Transactional
    @PostMapping("/offerToTopic")
    public CommonResult offerToTopic(@RequestBody JSONObject body) {
        String uId = request.getHeader("uId");
        Orders orders = body.getObject("orders", Orders.class);
        String payPassword = body.getString("payPassword");

        String topicId = orders.gettId();
        Object topicObject = redisTemplate.opsForValue().get(Constant.TOPIC_AUCTION + topicId);
        if (topicObject == null) {
            return CommonResult.build(Constant.OFFER_ERROR_TOPIC_INVALID, "主题无效");
        }
        Topic targetTopic = (Topic) topicObject;
        User user = getUser(uId);
        CommonResult commonResult = PayUtils.payPrepare(payPassword, targetTopic.getDeposit(), user);
        if (commonResult != null) {
            return commonResult;
        }
        Date currentDate = new Date();
        if (currentDate.before(targetTopic.getStartTime())) {

            return CommonResult.build(Constant.OFFER_ERROR_NOT_START, "拍卖还没开始");
        }
        if (currentDate.after(targetTopic.getStopTime())) {
            return CommonResult.build(Constant.OFFER_ERROR_HAD_STOP, "拍卖已经结束");
        }

        //判断自己对该拍卖主题是否有历史竞价记录

        //  Boolean member = redisTemplate.opsForSet().isMember(Constant.TOPIC_AUCTION_SET + topicId, uId);
        Object offerObject = redisTemplate.opsForValue().get(Constant.TOPIC_OFFER + topicId + ":" + uId);
        Orders lastOrders = null;
        if (offerObject == null) {
            orders.setId(IDUtils.genOrderItemId());
            orders.setuId(uId);
            orders.setTitle(targetTopic.getTitle());
            orders.setDeposit(targetTopic.getDeposit());//第一次生成出价的时候，保存进入 保证金
            orders.setStatus(Constant.ORDER_STATUS_INIT);
            orders.setType(Constant.ORDER_TYPE_TO_AUCTION_TOPIC);
            orders.setOfferCount(1);
            orders.setSellerId(targetTopic.getuId());
            orders.setCreated(currentDate);
        } else {
            lastOrders = (Orders) offerObject;
            orders.setId(lastOrders.getId());
            orders.setuId(uId);//必须把uId 给再次放进去，不然在定时任务中无法从offer 中拿到uId
            orders.setTitle(targetTopic.getTitle());
            orders.setDeposit(lastOrders.getDeposit());
            orders.setOfferCount(lastOrders.getOfferCount() + 1);
            orders.setSellerId(targetTopic.getuId());
            orders.setUpdated(currentDate);
        }

        BigDecimal offerAmount = orders.getAmount();
        RedisLock redisLock = new RedisLock(redisTemplate, Constant.TOPIC_AUCTION + topicId);
        try {

            if (!redisLock.lock()) {
                return CommonResult.build(Constant.OFFER_BUSY, "其他人正在出价");
            }
            BigDecimal originalPrice = targetTopic.getPrice();
            BigDecimal bidIncrement = targetTopic.getBidIncrement();
            BigDecimal deposit = targetTopic.getDeposit();//保证金
            if (originalPrice.add(bidIncrement).compareTo(offerAmount) > 0) {
                logger.warn("offerToTopic:出价过低,topicId:{},originalPrice:{},offerAmount:{}", topicId, originalPrice, offerAmount);
                return CommonResult.build(Constant.OFFER_ERROR_AMOUNT_LOWER, "出价过低");
            }
//            Map<Object, Object> userEntity = redisTemplate.opsForHash().entries(Constant.USER_SESSION + uId);
//            User user = JSONObject.parseObject(JSONObject.toJSONString(userEntity), User.class);
//            //如果用户账户可用余额小于该拍卖商品设置的保证金，直接返回
//
//            if (user.getBalance() - user.getDeposit() < deposit) {
//                logger.error("orders:余额不足 uid:{},originalBalance:{},deposit:{},offerAmount:{}", uId, user.getBalance(), user.getDeposit(), offerAmount);
//                return CommonResult.build(Constant.BALANCE_NOT_ENOUGH, "余额不足");
//            }
            targetTopic.setoId(orders.getId());
            targetTopic.setPrice(offerAmount);
            targetTopic.setUpdated(currentDate);
            //把改变了最高价的topic更新进mysql,否在在个人出价查询中获取的topic信息可能就是过时的
            Topic updateTopic = new Topic();
            updateTopic.setId(targetTopic.getId());
            updateTopic.setPrice(offerAmount);
            updateTopic.setUpdated(currentDate);
            topicMapper.updateByPrimaryKeySelective(updateTopic);
            JSONObject result = new JSONObject();
            if (lastOrders == null) {
                ordersMapper.insertSelective(orders);
                BigDecimal mysqlDeposit = userMapper.incrementDeposit(uId, deposit);//第一次对拍卖商品出价才增加用户的保证金，因为拍卖商品的保证金是发布者指定的，跟出价金额无关，后续的继续叫价，不再涉及保证金问题
                redisTemplate.opsForSet().add(Constant.TOPIC_AUCTION_SET + topicId, uId);//标记用户对此topic 有过出价
                redisTemplate.opsForHash().put(Constant.USER_SESSION + uId, Constant.DEPOSIT, mysqlDeposit);//同步缓存中的保证金
                logger.info("offerToTopic:保证金增加 uId:{},offerId:{},amount:{},deposit:{}", uId, orders.getId(), deposit, mysqlDeposit);
                result.put("deposit", mysqlDeposit);
                result.put("offerCount", 1);

            } else {
                ordersMapper.updateByPrimaryKeySelective(orders);
                result.put("offerCount", orders.getOfferCount());
            }
            redisTemplate.opsForValue().set(Constant.TOPIC_OFFER + topicId + ":" + uId, orders);
            redisTemplate.opsForValue().set(Constant.TOPIC_AUCTION + topicId, targetTopic);//把更改了最高价的主题信息保存进redis,如果要在客户端的主题详情中直接显示最高价的话，还需要更新进ES
            return CommonResult.ok(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
            logger.error("offerToTopic:出价失败:{}", e.getMessage());
            return CommonResult.build(Constant.UNKNOWN_ERROR, "未知错误");
        } finally {
            redisLock.unlock();
        }

    }


    @RequestMapping(value = "/queryTopicOffer", method = RequestMethod.POST)
    public CommonResult queryTopicOffer(@RequestBody JSONObject body) {
        String topicId = body.getString("topicId");
        //Object uId = info.get("uId");
        int page = 1;
        int size = 10;
        if (body.containsKey("page")) {
            page = body.getInteger("page");
        }
        if (body.containsKey("size")) {
            size = body.getInteger("size");
        }
        PageHelper.startPage(page, size);
        List<Orders> orders = ordersMapper.queryOrderByTopicId(topicId);
        PageInfo<Orders> offerPageInfo = new PageInfo<>(orders);
        return CommonResult.ok(offerPageInfo);
    }

    @SerializedField(encrypt = false)
    @PostMapping("/queryHottestTopic")
    public CommonResult queryHottestTopic(@RequestBody JSONObject body) {
        int page = 1;
        int size = 10;
        if (body.containsKey("page")) {
            page = (int) body.get("page");
        }
        if (body.containsKey("size")) {
            size = (int) body.get("size");
        }
        List topicList = redisTemplate.opsForList().range("HOTEST_TOPIC", page * size - size, page * size);
        return CommonResult.ok(topicList);
    }


    private boolean isPhoneNumber(String mobiles) {

        Pattern p = Pattern.compile(phoneNumberMatch);
        Matcher m = p.matcher(mobiles);
        return m.matches();
    }

    private User getUser(String uId) {
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(Constant.USER_SESSION + uId);
        if(entries.isEmpty()){
            return  userMapper.selectByPrimaryKey(uId);
        }
        return JSONObject.parseObject(JSONObject.toJSONString(entries), User.class);
    }

    /**
     * 没5分钟执行一次的Key
     *
     * @param date
     * @return
     */
    private String generateKeyFiveMinute(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int minute = calendar.get(Calendar.MINUTE);
        int num = minute % 10;
        if (num < 5) {
            minute = minute + (5 - num);

        } else {
            minute = minute + (10 - num);
        }
        calendar.set(Calendar.MINUTE, minute);
        Date time = calendar.getTime();
        return DateUtils.formatDate(time, "yyyy-MM-dd-HH-mm");
        //如果分钟的尾数小于5，就让它等于5，如果大于5，就进位处理，
        // 例子原值为： 2019-03-25-17-03，转化为：2019-03-25-17-05，原值：2019-03-25-17-15，转化为 ：2019-03-25-17-20，
        // 因为在定位任务中是没5分钟执行一次 拍卖结算，如果更改结算频率，则这儿也需要修改
//        String endMinite = key.substring(15);
//        if (Integer.valueOf(endMinite) < 5) {
//            endMinite = "5";
//            key = key.substring(0, 15) + endMinite;
//        } else {
//            endMinite = "0";
//            Integer reverseSEcondMinute = Integer.valueOf(key.substring(14, 15));
//            reverseSEcondMinute++;
//            key = key.substring(0, 14) + String.valueOf(reverseSEcondMinute) + endMinite;
//        }
//
//        return key;
    }

    /**
     * 没1分钟执行一次的Key
     * 根据到期时间，增加1分钟，作为缓存的key
     *
     * @param date
     * @return
     */
    private String generateKeyOneMinute(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        int minute = calendar.get(Calendar.MINUTE);
        calendar.set(Calendar.MINUTE, minute + 1);
        Date time = calendar.getTime();
        return DateUtils.formatDate(time, "yyyy-MM-dd-HH-mm");

    }

    /**
     * 使用负载均衡算法获取处理当前审核主题的管理员信息
     *
     * @return
     */
    private String getTopicAdminId() {
        count = MytxConfig.topicAdminList.size();
        int index = reqCount++ % count;
        String adminId = MytxConfig.topicAdminList.get(index);
        logger.info("当前审核主题负责的管理员:adminId:{}", adminId);
        return adminId;
    }
}
